<#
    .AUTHOR
        cw50406@imcnam.ssmb.com  
    .GROUP
        RPA AA STE Support Team(dl.gt.cn.cstc.rpa.aa.ste@imcnam.ssmb.com)
    .VERSION
        1.0
    .Synopsis
        Control Room(CR) User Access Management
    .DESCRIPTION
        This script is used to automatically manage user access(create,update,delete,enable,diable) in cr website by posting a jason body to the server.
    .NOTES
        This script depends on below items to be ran successfully.
        1. The user who runs this script must have enough access to take the response action in the destination CR, otherwise a separated user credetials must be provided
        2. At least read access is required to the responding data base to get user/role details
    .EXAMPLE
        .\UserManagement.ps1
        try to take user request actions by the given csv list in user_manage_request_list.csv in the same directory
    .EXAMPLE
        .\UserManagement.ps1 -request_csv_file "c:\temp\userlist.csv"
        try to take user request actions from "c:\temp\userlist.csv"
#>
[CmdletBinding()]param(
    [Parameter(Mandatory=$false)]
    [ValidateScript({Test-Path $_})]
    [string]$request_csv_file='.\user_manage_request_list.csv',        # where the script will read the request list 
    [switch]$inputFileSaved,                                           # on : script will ask user to enter value if $request_csv_file content is empty
    [switch]$functionInvoked,                                          # off: script will re-invoke functions.ps1 in this script folder 
    [switch]$noninteractive,                                           # off: script will ask user to enter value if $request_csv_file content is empty
    [switch]$use_diff_user,                                            # on: script will use another user credentials to post http request 
    [ValidatePattern("^(nam|apac|eur)\\.{7,9}")]                       #
    [string]$diff_user,                                                # the other user name
    [string]$diff_user_pwd                                             # the other user password 
)
#<----------------------------------------------------------------------------------------------------initilize params
If( !$functionInvoked ){                                                     #<------------------- initilize functions
    Try{                                                                     
        Try{
            $this_path = Split-Path -Parent ($MyInvocation.MyCommand.Definition) -ea 1
        }Catch{
            $this_path = [System.IO.Path]::GetDirectoryName([System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName)
        }Finally{}
        . "$this_path\functions.ps1" # import functions to powershell from functions.ps1
    }Catch{
       $_; exit
    }Finally{}
}                                                                            
If( ($rst_l=New-FileOrFolder "$this_path\log" -d $um_log_file) -eq $true){   #<----------- initilize log path and file
    If( ($rst_u=New-FileOrFolder "$this_path\log\UserManagement" -d $um_log_file) -eq $true){
        $um_log_file="$this_path\log\UserManagement\{0:yyyyMMdd_HHmmss}_CRUserManagement.log" -f (get-date)
    } else{
        Pop-Message $rst_u 'IO Create Error' -log_file $um_log_file -noninteractive $noninteractive ;exit 
    }
}else{
    Pop-Message $rst_l 'IO Create Error' -log_file $um_log_file -noninteractive $noninteractive ; exit 
} 
Try{                                                                         #<-------------------- initilize csv file
    If( ($rst_c=New-FileOrFolder $request_csv_file $um_log_file) -eq $true ){
        $request_csv_file=(gi $request_csv_file).FullName ; $request_csv_list=ipcsv $request_csv_file -ea 1
        If(($PSBoundParameters.ContainsKey('inputFileSaved') -and $request_csv_list -eq $null) -or !$inputFileSaved ){
            If( !$noninteractive){
                clear-content $request_csv_file -force -ea 1
                Do{ # loop above action if the vdi list is empty 
                    write-host "Please save request list in opened csv file each line as it's empty, please press 'Ctrl+C' if you want to quit"
                    $notepad_process=Start-Process notepad -ArgumentList $request_csv_file -PassThru ; $notepad_process.WaitForExit()
                    $request_csv_list = ipcsv $request_csv_file -ea 1
                }While( $request_csv_list -eq $null  )
            }else{ # send emails
                write-log Error "Script stopped cause $request_csv_file is empty!" $um_log_file; exit
            }
        }
    }else{
        Pop-Message $rst_c 'IO Create Error' -log_file $um_log_file -noninteractive $noninteractive ; exit 
    }
}Catch{
    write-log Error $_.exception $um_log_file
    Pop-Message "Please check $um_log_file for details" 'Request Input File Read Error' -log_file $um_log_file -noninteractive $noninteractive ; exit 
}Finally{}
$ms_string=Get-MSFromEpoch $um_log_file
Try{                                                                         #<--------- initilize db server list file, might move to funtions.ps1
    $db_server_list_file="$this_path\db_server_list.txt"
    If( ($rst_d=New-FileOrFolder $db_server_list_file -log_file $um_log_file) -eq $true ){
        $db_server_list=cat "$this_path\db_server_list.txt" -ea 1
        If(($PSBoundParameters.ContainsKey('inputFileSaved') -and $db_server_list.count -eq 0) -or !$inputFileSaved ){
            If( !$noninteractive){
                clear-content $db_server_list_file -force -ea 1
                Do{ # loop above action if the vdi list is empty 
                    write-host "Please save db server list in opened txt file each line as it's empty, please press 'Ctrl+C' if you want to quit"
                    notepad $db_server_list_file ; read-host " "
                    $db_server_list = (get-content $db_server_list_file |select -Unique|?{$_.trim() -ne ""}|sort)
                }While( $db_server_list -eq $null  )
            }else{ # send emails
                write-log Error "Script stopped cause $db_server_list_file is empty!" $um_log_file; exit
            }
        }
    }else{
        Pop-Message $rst_d 'IO Create Error' -log_file $um_log_file -noninteractive $noninteractive ; exit 
    }
}Catch{
    write-log Error $_.exception $um_log_file
    Pop-Message "Please check $um_log_file for details" 'DB Input File Read Error'  -log_file $um_log_file -noninteractive $noninteractive; exit 
}Finally{}
$db_server_list|% {$db_server_hash=@{}} {$key,$value=$_.split('=');$db_server_hash.$key=$value} # might move to funtions.ps1
If( ($headers = New-Headers -log_file $um_log_file) -eq $null){              #<--------- initilize http request header
    Pop-Message "Please check $um_log_file for details" 'HTTP Request Header Create Error' -log_file $um_log_file -noninteractive $noninteractive; exit
}
$user_manage_action={                                                        #<--------- the actual action for paralle threads
    param($list,$cnt,$addition_params)
    #<--------------------------------------initilize params & invoke functions 
    $db_domain,$db_env,$fid,$roles_list,$licenses_list,$action_type=$list.DBDomain,$list.DBEnv,$list.FID,$list.Roles,$list.Licenses,$list.ActionType
    $function_file,$db_server_hash,$headers,$ms_string,$um_log_file=$addition_params
    . $function_file
    $db_server=$db_server_hash."$db_domain$db_env"
    write-host "Processing `"$action_type`" action on <<$cnt FID: $fid in $db_domain-$db_env control room>>`n"
    If( $fid -inotmatch "^(nam|apac|eur)\\.{7,9}" ){                          #<--------------- validate and check FID existence in AD
        write-log Error "$cnt FID $fid not matched patten '^(nam|apac|eur)\\.{7,9}'" $um_log_file ; return
    }else{$domain,$fid=$fid.split('\')}
    Try{
        [ADSI]::Exists("WinNT://$domain/$fid,user") 1>$null
    }Catch{
        write-log Error ("$cnt FID: $fid--{0}" -f $_.Exception.Message) $um_log_file ; return
    }Finally{}
    If(($db_pwd=[CredentialValidate.GetPassword]::ReturnDBPWD($db_domain,$db_env)) -eq $null){ # validate DB password and create the DB connection
        write-log Error "Unable to get valid password for $db_domain,$db_env" $um_log_file ;return 
    }
    If( ($db_connection=Connect-Sql $db_server $db_pwd $um_log_file) -eq $null){ 
        write-log Error "$cnt FID: $fid<--DB connection failed-->$db_server" $um_log_file ; return
    }
    $cr_lb_url=Switch -regex ($db_env){                                                #<----------- initilize controlroom load balance site url
        "poc|lab"{"http://icg-aa-cr-$db_env-lb.$db_domain.nsroot.net/controlroom";break}
        default  {"http://icg-aa-cr-$db_env.$db_domain.nsroot.net/controlroom"         }
    }
    #<--------------------------------------initilize request jaso hashtable depends on the request type
    If( $action_type -in @("enable","disable","delete") ){ # take relevent actions for "ENABLE","DISABLE","DELETE" REQUEST
        If(($db_fid_detail=Execute-Sql $db_connection "SELECT Id,IsLocked FROM [dbo].[Users] where UserName='$fid' and IsDeleted=0" $um_log_file) -eq $null){
            write-log Error "$cnt FID: $fid<--DB check FID sql query return NULL, either invalid query or no such FID mathed-->$db_server`n" $um_log_file -print_to_console ;  $db_connection.close() ; return
        }else{
            write-log Success "Found non-deleted FID: $fid in $db_domain $db_env DB" $um_log_file
            If( 'enable' -ieq $action_type -and (0 -eq $db_fid_detail.IsLocked )){
                write-log Success "$cnt FID: $fid was found already in enabled status in $db_domain $db_env DB`n" $um_log_file -print_to_console; return
            }elseif('disable' -ieq $action_type -and (1 -eq $db_fid_detail.IsLocked )){
                write-log Success "$cnt FID: $fid was found already in disbled status in $db_domain $db_env DB`n" $um_log_file -print_to_console; return
            }
            $cr_query_url = $cr_lb_url.replace("controlroom",("webcrsvc/Users/get/All?_dc" + (iex $ms_string) +"&page=1&start=0&limit=25"))
            $jason_hash=(Invoke-WebRequest -Uri $cr_query_url -UseDefaultCredentials |ConvertFrom-Json).Value |?{$_.UserName -eq $fid}
            $jason_hash.IsLocked=Switch($action_type){"enable"{$false} "disable"{$true}}
            $additon_fid_property=@{
                'Machine Id'=''
                IsActive= Switch($action_type){
                    "enable"  {'Deactivate' ;break}
                    "disable" {'Activate'   ;break}
                    "delete"  {Switch ($db_fid_detail.IsLocked){$true {'Inactive'} $false {'Active'}}}
                }
                UserStatus=Switch ($db_fid_detail.IsLocked){$true {'Active'} $false {'Inactive'}}
                Status='Registered' # might to be updated as we found this valus is Verified in NAM POC CR jason file
                DisplayRoles=$jason_hash.Roles.value -join ', '
                Features= Switch ($jason_hash.LicenseFeatures -join ','){ # currently not included IQBots
                    "FuncRuntime,MetabotRuntime"{"RunTime (TaskBots, MetaBots)";break}
                    "FuncRuntime"               {"RunTime (TaskBots)";break}
                    "Development"               {'Development';break}
                    default                     {''}
                }
                DisplayUserName="$domain\$fid"
            }
            $additon_fid_property.GetEnumerator()|%{Add-Member -InputObject $jason_hash -MemberType NoteProperty -Name $_.name -Value $_.value}
        }
        # $jason_hash | ConvertTo-Json -Compress ; return 
    }else{ # take relevent actions for CREATE AND UPDATE REQUEST
        $jason_hash=@{ # to be converted to jason format for post body use
            AdDomain=$domain  # NAM, EUR, APAC
            DomainId=1
            Email=''
            EnableAutoLogin=$true
            FirstName=''
            IsLocked=$false
            LastName=''
            licenseFeatures=@()
            Password=''
            Roles=@()
            'um_ConfirmPassword-inputEl'=''
            'um_Roles-inputEl'=@()
            Username=$fid
        }
        $fid_adobject=Get-ADObject "$domain\$fid" $um_log_file
        $jason_hash.FirstName=$fid_adobject.givenname[0]
        $jason_hash.LastName=$fid_adobject.sn[0]
        $jason_hash.Email=$fid_adobject.mail[0]
        Foreach( $r in $roles_list.split('|')){ # update hashtable with roles key
            #<------------------------get each role details from DB server
            If( ($db_role_detail=Execute-Sql $db_connection "SELECT Id,Name FROM [ICGDB].[dbo].[Roles] where Name = '$r'" $um_log_file) -eq $null ){
                write-log Error "Failed to find role $r in $db_domain\$db_env DB-->$DBServer`n" $um_log_file -print_to_console; $db_connection.close() ; return
            }else{
                $jason_hash.'um_Roles-inputEl'+=$db_role_detail.ID ; $jason_hash.Roles+=@{Key=$db_role_detail.ID;Value=$r}
            }
        }
        $licenses_list.split('|')|%{$jason_hash.licenseFeatures+=$_} # # update hashtable with licenses key
        If( $action_type -ieq "update" ){ 
            write-log Normal "$cnt FID: $fid--Trying to connect DB and get FID ID which will be used in the post body for update request" $um_log_file
            #<------------------------get fid details from DB server
            If(($db_fid_detail=Execute-Sql $db_connection "SELECT Id,IsLocked FROM [ICGDB].[dbo].[Users] where UserName ='$fid' and IsDeleted =0" $um_log_file) -eq $null){
                write-log Error "$cnt FID: $fid<--DB sql query failed-->$db_server`n" $um_log_file -print_to_console; $db_connection.close() ; return
            }else{
                write-log Success "Found FID $fid ID $($db_fid_detail.ID) in $db_domain $db_env DB" $um_log_file
                $jason_hash.Id=$db_fid_detail.ID
            }
        }
    } 
    $replace_string = Switch -regex ($action_type){ # to be optimized
        "^update$"           {"Update" ;break} #updated user access url
        "^create$"           {"add" ;break}
        "^(enable|disable)$" {"Update?_dc=" + (iex $ms_string) ;break}
        "^delete$"           {"Delete?_dc=" + (iex $ms_string) ;break}
        default              {write-log Error "The $cnt CR requst type `"$_`" is not valid!`n" $um_log_file -print_to_console; return }
    }
    $cr_request_url = $cr_lb_url.replace("controlroom","webcrsvc/Users/$replace_string")
    #<--------------- the actual action part for the request with instructure jason hashtable
    Try{
        $cr_action_rst=Invoke-WebRequest -Uri $cr_request_url -UseDefaultCredentials -Headers $headers -ContentType "application/json" -Method Post -Body ($jason_hash| ConvertTo-Json -Compress) -UseBasicParsing
        If( $cr_action_rst.StatusCode -eq "200"){
            write-host -fore yellow "Done `"$action_type`" action for <<$cnt FID: $fid in $db_domain-$db_env control room>>`n"
            write-log Success "Successfully `"$action_type`" <<$cnt FID: $fid in $db_domain-$db_env control room>>" $log_file
        }else{
            write-log Error ("$cnt FID `"$action_type`" http request failed with error: StatusCode-{0} Content-{1}" -f $cr_action_rst.StatusCode,$cr_action_rst.Content) $um_log_file
        }
    }Catch{
        write-log Error $_.Exception.Message $um_log_file
    }Finally{}
    #< =============================== double check DB that the request do have taken effectively =============================== >#
    #$user_access_query= 
    #	"select '$domain' as DBDomain,'$env' as DBEnv,dc.HostName, du.ADNetBIOS as Region,du.UserName, dr.Name as Roles, df.Feature as licenses
    #	from [ICGDB].[dbo].[Clients] dc,[ICGDB].[dbo].[Users] du,[ICGDB].[dbo].[Roles] dr,[ICGDB].[dbo].[UserRoles] dur,[ICGDB].[dbo].[Features] df
    #	where du.IsDeleted=0
    #    and dc.UserId=du.id
    #	and du.id=dur.UserId 
    #	and dr.id=dur.RoleId 
    #	and df.UserId=du.id"
    #$user_access_detail=Execute-Sql $db_connection $user_access_query $um_log_file
    #If( $user_access_detail.Roles -eq $roles_list -and $user_access_detail.licenses -eq $licenses_list){
    #    return $true
    #}else{
    #    write-log Error 'The $cnt user access in DB was found not matched to the given list even the http request was posted successfully.' $um_log_file
    #}
    Try{
        $db_connection.close()
    }Catch{
        write-log Error $_.Exception.Message $um_log_file
    }Finally{}
}
Start-BulkThreads $request_csv_list $user_manage_action "$this_path\functions.ps1",$db_server_hash,$headers,$ms_string,$um_log_file
